///******************** NG.Respate(C)  **********************************
//* 产品名称: Respate 科技
//* 文件名称: signal.c
//* 程序作者: 雷帕特公司(南宫子萍)
//* 程序版本: V1.1														   
//* 编制日期: 2019/1/29
//* 功能描述: tftp服务器
//* QQ:	2085827949
//**********************************************************************/

#include "lap_tftp.h"

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// tftp 配置
tftpConfig tConfig =
{
	.port = 69,
	.ip = "123.123.123.1",
	.block = 512,
	.mode = TFTP_MODE_NETAS,
	.timeout = 1000*900,
	.retimes = 3,
	.download = "./bin_dir"
};
/*********************************************************************************
** 函数名称: tftp_config
** 功能描述: 设置服务器
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
int tftp_config(tftpConfig* config)
{
	// tConfig = *config;
	if(config->port!=0) 		tConfig.port 	= config->port;
	if(*config->ip!=NULL) 		strcpy(tConfig.ip,config->ip);
	if(config->block!=0) 		tConfig.block 	= config->block;
	if(*config->mode!=NULL) 		strcpy(tConfig.mode,config->mode);
	if(config->timeout!=0) 		tConfig.timeout = config->timeout;
	if(config->retimes!=0) 		tConfig.retimes = config->retimes;
	if(*config->download!=NULL)  strcpy(tConfig.download,config->download);
	return 0;
}

/*********************************************************************************
** 函数名称: tftp_printfConfig
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
int tftp_printfConfig(tftpConfig* config)
{
	printf("tftpConfig Info:\r\n");
	printf("port: %d\r\n",config->port);
	printf("ip: %s\r\n",config->ip);
	printf("block: %d\r\n",config->block);
	printf("mode: %s\r\n",config->mode);
	printf("timeout: %d\r\n",config->timeout);
	printf("retimes: %d\r\n",config->retimes);
	printf("download: %s\r\n",config->download);
}


/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
// 临时打印信息
void test_printf(char* buff,int len)
{
	for (int i = 0; i < len; i++)
	{
		if(buff[i]=='\0') putchar('_');
		else putchar(buff[i]);
	}
	putchar('\n');
	printf("len:%d\r\n",len);
}


/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
// 1.TFTP 读写请求包
int tftp_PackRQ(tftp_pRrw* t_rwq,char* buff)
{
	int len =0;

	// 构造数据请求包
	*(short*)buff = htons(t_rwq->opcode);			len+=2;		// 操作码转网络字节序
	len += sprintf(&buff[len],"%s",t_rwq->name);	len++;		// 文件名
	len += sprintf(&buff[len],"%s",t_rwq->mode);	len++;		// 最后一字节为'\0'
	buff[len] = '\0';
	return len;
}



/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
// 3.TFTP 文件数据包
int tftp_PackData(tftp_pData* t_data,char* buff,int num)
{
	int len =0;

	// 文件数据包
	*(short*)buff = htons(t_data->opcode);			len+=2;		// 操作码转网络字节序
	*(short*)&buff[len] = htons(t_data->block);		len+=2;		// 块
	memcpy(&buff[len],t_data->data,num);			len+=num-1;	// 
	return len;
}

// 4.TFTP 回应包(ACK)
int tftp_PackAck(tftp_pAck* t_ack,char* buff)
{
	int len =0;

	// 文件数据包
	*(short*)buff = htons(t_ack->opcode);			len+=2;		// 操作码转网络字节序
	*(short*)&buff[len] = htons(t_ack->block);		len+=2;		// 块
	return len;
}

// 5.TFTP 错误信息包
int tftp_PackErr(tftp_pErr* t_err,char* buff,int num)
{
	int len =0;

	*(short*)buff = htons(t_err->opcode);			len+=2;		// 操作码 转网络字节序
	*(short*)&buff[len] = htons(t_err->errcode);	len+=2;		// 错误码 转网络字节序
    memcpy(&buff[len],t_err->errmsg,num);			len+=num;	// 错误信息
	buff[len] = '\0';	// 最后一字节为'\0'
	return len;
}


/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
// 数据包转数据流
int tftp_Pack2Stream(const tftpPackage* pack,char* buff,int num)
{
	int len = 0;
	short code = *(short*)pack; 		// 获取操作码

	switch (code)
	{
	case TFTP_RRQ:	// 1.读文件请求包
		return tftp_PackRQ((tftp_pRrw*)pack,buff);
		break;

	case TFTP_WRQ:	// 2.写文件请求包
		return tftp_PackRQ((tftp_pRrw*)pack,buff);
		break;

	case TFTP_DATA:	// 3.文件数据包
		return tftp_PackData((tftp_pData*)pack,buff,num);
		break;

	case TFTP_ACK:	// 4.回应包(ACK)
		return tftp_PackAck((tftp_pAck*) pack, buff);
		break;

	case TFTP_ERROR:// 5.错误信息包
		return tftp_PackErr((tftp_pErr*)pack,buff,num);
		break;
	
	// default:
	// 	break;
	}
	return 0;
}


/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
int tftp_Stream2Pack(tftpPackage* pack,const char* buff,int num)
{
	int len = 0;
	short code = ntohs(*(short*)buff); 		// 获取操作码
	pack->pdata.opcode = code;				// 保存操作码

	switch (code)
	{
	case TFTP_RRQ:	// 1.读文件请求包
		// return tftp_PackRQ((tftp_pRrw*)pack,buff);
		// break;

	case TFTP_WRQ:	// 2.写文件请求包
		len = 2;
		pack->prw.name = &buff[len];
		len += strlen(&buff[len]);
		pack->prw.mode = &buff[len+1];
		return 0;
		break;

	case TFTP_DATA:	// 3.文件数据包
		pack->pdata.block = htons(*(short*)&buff[2]);	// 当前块号
		pack->pdata.data = (char*)&buff[4];	// 得到数据的起始地址
		return num -4;	// 返回数据长度
		break;

	case TFTP_ACK:	// 4.回应包(ACK)
		pack->pack.block = htons(*(short*)&buff[2]);	// 当前块号
		return 0;
		break;

	case TFTP_ERROR:// 5.错误信息包
		pack->perr.errcode = htons(*(short*)&buff[2]);	// 获取错误码
		pack->perr.errmsg = (char*)&buff[4];			// 得到错误信息起始地址
		return num -4;	// 返回信息长度
		break;
	
	// default:
	// 	break;
	}
	return -1;
}



/*********************************************************************************
** 函数名称: tftp_packInfo
** 功能描述: 打印包信息
** 输入参数: fd: 文件流  pack:信息包	len:接收长度        
** 输出参数: None.
** 返回参数: return response .
**********************************************************************************/
void tftp_packInfo(FILE* fd,tftpPackage* pack,int len)
{
	fprintf(fd,"\r\nRecve len:%d\r\n",len);
	short code = pack->pdata.opcode; // 保存操作码
	switch (code)
	{
	case TFTP_RRQ:	// 1.读文件请求包
		fprintf(fd,"opcode:TFTP_RRQ \r\n");
		fprintf(fd,"file name:%s \r\n",pack->prw.name);
		fprintf(fd,"mode:%s \r\n",pack->prw.mode);
		break;

	case TFTP_WRQ:	// 2.写文件请求包
		fprintf(fd,"opcode:TFTP_WRQ \r\n");
		fprintf(fd,"file name:%s \r\n",pack->prw.name);
		fprintf(fd,"mode:%s \r\n",pack->prw.mode);
		break;

	case TFTP_DATA:	// 3.文件数据包
		fprintf(fd,"opcode:TFTP_DATA \r\n");
		fprintf(fd,"block:%d \r\n",pack->pdata.block);
		fprintf(fd,"data:%s \r\n",pack->pdata.data);
		break;

	case TFTP_ACK:	// 4.回应包(ACK)
		fprintf(fd,"opcode:TFTP_ACK \r\n");
		fprintf(fd,"block:%d \r\n",pack->pack.block);
		break;

	case TFTP_ERROR:// 5.错误信息包
		fprintf(fd,"opcode:TFTP_ERROR \r\n");
		fprintf(fd,"errCode:%d \r\n",pack->perr.errcode);
		fprintf(fd,"errMsg:%s \r\n",pack->perr.errmsg);
		break;
	
	default:
		fprintf(fd,"pre err cdoe:%d\r\n",code);
		break;
	}
}
